/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package java.util.jar;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * The input stream from which the JAR file to be read may be fetched. It is
 * used like the {@code ZipInputStream}.
 *
 * @see ZipInputStream
 */
public class JarInputStream extends ZipInputStream {

	private Manifest manifest;

	private boolean eos = false;

	private JarEntry mEntry;

	private JarEntry jarEntry;

	private boolean isMeta;

	private OutputStream verStream;

	/**
	 * Constructs a new {@code JarInputStream} from an input stream.
	 *
	 * @param stream the input stream containing the JAR file.
	 * @param verify if the file should be verified with a {@code JarVerifier}.
	 * @throws IOException If an error occurs reading entries from the input stream.
	 * @see ZipInputStream#ZipInputStream(InputStream)
	 */
	public JarInputStream(InputStream stream, boolean verify) throws IOException {
		super(stream);
		if (verify) {
		}
		if ((mEntry = getNextJarEntry()) == null) {
			return;
		}
		if (mEntry.getName().equalsIgnoreCase(JarFile.META_DIR)) {
			mEntry = null; // modifies behavior of getNextJarEntry()
			closeEntry();
			mEntry = getNextJarEntry();
		}
		if (mEntry.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME)) {
			mEntry = null;
			manifest = new Manifest(this, verify);
			closeEntry();


		} else {
			Attributes temp = new Attributes(3);
			temp.map.put("hidden", null);
			mEntry.setAttributes(temp);
			/*
             * if not from the first entry, we will not get enough
             * information,so no verify will be taken out.
             */
		}
	}

	/**
	 * Constructs a new {@code JarInputStream} from an input stream.
	 *
	 * @param stream the input stream containing the JAR file.
	 * @throws IOException If an error occurs reading entries from the input stream.
	 * @see ZipInputStream#ZipInputStream(InputStream)
	 */
	public JarInputStream(InputStream stream) throws IOException {
		this(stream, true);
	}

	/**
	 * Returns the {@code Manifest} object associated with this {@code
	 * JarInputStream} or {@code null} if no manifest entry exists.
	 *
	 * @return the MANIFEST specifying the contents of the JAR file.
	 */
	public Manifest getManifest() {
		return manifest;
	}

	/**
	 * Returns the next {@code JarEntry} contained in this stream or {@code
	 * null} if no more entries are present.
	 *
	 * @return the next JAR entry.
	 * @throws IOException if an error occurs while reading the entry.
	 */
	public JarEntry getNextJarEntry() throws IOException {
		return (JarEntry) getNextEntry();
	}

	/**
	 * Reads up to {@code byteCount} bytes of decompressed data and stores it in
	 * {@code buffer} starting at {@code byteOffset}. Returns the number of uncompressed bytes read.
	 *
	 * @throws IOException if an IOException occurs.
	 */
	@Override
	public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
		if (mEntry != null) {
			return -1;
		}
		int r = super.read(buffer, byteOffset, byteCount);
		if (verStream != null && !eos) {
			if (r == -1) {
				eos = true;
			} else {
				verStream.write(buffer, byteOffset, r);
			}
		}
		return r;
	}

	/**
	 * Returns the next {@code ZipEntry} contained in this stream or {@code
	 * null} if no more entries are present.
	 *
	 * @return the next extracted ZIP entry.
	 * @throws IOException if an error occurs while reading the entry.
	 */
	@Override
	public ZipEntry getNextEntry() throws IOException {
		if (mEntry != null) {
			jarEntry = mEntry;
			mEntry = null;
			jarEntry.setAttributes(null);
		} else {
			jarEntry = (JarEntry) super.getNextEntry();
			if (jarEntry == null) {
				return null;
			}
		}
		eos = false;
		return jarEntry;
	}

	@Override
	protected ZipEntry createZipEntry(String name) {
		JarEntry entry = new JarEntry(name);
		if (manifest != null) {
			entry.setAttributes(manifest.getAttributes(name));
		}
		return entry;
	}
}
